
/*-------------------------------------------------------------------------
 *
 * sharedfileset.c
 *	  共享临时文件管理。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/storage/file/sharedfileset.c
 *
 * SharedFileSets 提供一个临时命名空间（考虑为目录），以便
 * 文件可以通过名称被发现，并提供共享所有权语义，使得
 * 共享文件在最后一个用户分离之前能够存活。
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <limits.h>

#include "catalog/pg_tablespace.h"
#include "commands/tablespace.h"
#include "common/hashfn.h"
#include "miscadmin.h"
#include "storage/dsm.h"
#include "storage/ipc.h"
#include "storage/sharedfileset.h"
#include "utils/builtins.h"

static void fc_SharedFileSetOnDetach(dsm_segment *fc_segment, Datum fc_datum);

/*
 * 初始化一个可以被其他后端打开的临时文件空间。
 * 其他后端必须在访问之前附加到它。与 'seg' 关联此
 * SharedFileSet。任何包含的文件将在最后一个后端
 * 分离时删除。
 *
 * 在内部，这个集合是一个或多个最终将被删除的目录。
 */
void SharedFileSetInit(SharedFileSet *fc_fileset, dsm_segment *fc_seg)
{
	/* 初始化共享文件集特定成员。 */
	SpinLockInit(&fc_fileset->mutex);
	fc_fileset->refcnt = 1;

	/* 初始化文件集。 */
	FileSetInit(&fc_fileset->fs);

	/* 注册我们的清理回调。 */
	if (fc_seg)
		on_dsm_detach(fc_seg, fc_SharedFileSetOnDetach, PointerGetDatum(fc_fileset));
}

/*
 * 附加到使用 SharedFileSetInit 创建的一组目录。
 */
void SharedFileSetAttach(SharedFileSet *fc_fileset, dsm_segment *fc_seg)
{
	bool		fc_success;

	SpinLockAcquire(&fc_fileset->mutex);
	if (fc_fileset->refcnt == 0)
		fc_success = false;
	else
	{
		++fc_fileset->refcnt;
		fc_success = true;
	}
	SpinLockRelease(&fc_fileset->mutex);

	if (!fc_success)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("could not attach to a SharedFileSet that is already destroyed")));

	/* 注册我们的清理回调。 */
	on_dsm_detach(fc_seg, fc_SharedFileSetOnDetach, PointerGetDatum(fc_fileset));
}

/* 删除集合中的所有文件。 */
void SharedFileSetDeleteAll(SharedFileSet *fc_fileset)
{
	FileSetDeleteAll(&fc_fileset->fs);
}

/*
 * 当此后端从持有其创建或附加的 SharedFileSet 的 DSM 段分离时，将调用的回调函数。
 * 如果我们是最后一个分离的，则尝试删除目录及其
 * 所有内容。我们不能在失败时引发错误，因为这在
 * 错误清理路径中运行。
 */
static void fc_SharedFileSetOnDetach(dsm_segment *fc_segment, Datum fc_datum)
{
	bool		fc_unlink_all = false;
	SharedFileSet *fc_fileset = (SharedFileSet *) DatumGetPointer(fc_datum);

	SpinLockAcquire(&fc_fileset->mutex);
	Assert(fc_fileset->refcnt > 0);
	if (--fc_fileset->refcnt == 0)
		fc_unlink_all = true;
	SpinLockRelease(&fc_fileset->mutex);

	/*
	 * 如果我们是最后一个分离的，则删除所有
	 * 表空间中的目录。请注意，在此函数的其余部分中我们仍然实际上附加到此，
	 * 因此我们可以安全访问其数据。
	 */
	if (fc_unlink_all)
		FileSetDeleteAll(&fc_fileset->fs);
}
